//
// Project werewolf: night_context.cpp
// Created by alfielin on 2021/11/14.
//

#pragma once

#include "fake_voter.cpp"
#include <unordered_map>
#include <utility>
#include <vector>
#include <string>

namespace nz {
namespace werewolf {

class RoomContext : public std::enable_shared_from_this<RoomContext> {
private:

public:

    std::unordered_map<std::string, UserStatus> users;

    std::vector<std::vector<std::string>> night;  // night[role] = [userids]
    bool poisoned_tonight = false;
    bool used_cure = false;
    bool used_poison = false;

    FakeVoter wolf_voter;
    FakeVoter exile_voter;

public:

    RoomContext() : std::enable_shared_from_this<RoomContext>() {
        wolf_voter.init({}, [this](std::vector<std::string> targets) {
            this->night[int(RoleT::WOLF)] = std::move(targets);
        });
        exile_voter.init({}, [this](std::vector<std::string> targets) {
            this->users.at(targets[0]).kill(true, true);
        });
    }

    void init() {
        clear_night();
        used_cure = false;
        used_poison = false;
    }

    void clear_night() {
        night.clear();
        night.resize(int(RoleT::UGLY_COUNT_));
        poisoned_tonight = false;
        wolf_voter.clear();
        exile_voter.clear();
        for (auto &pp: users) {
            pp.second.clear();
        }
    }

    void set_couple(const std::string &a, const std::string &b) {
        users.at(a).lover = &users.at(b);
        users.at(b).lover = &users.at(a);
    }

    void commit_night() {
        auto death_list = get_night_death_list();
        for (const auto &userid: death_list) {
            users.at(userid).kill(false, false);
        }
    }

    std::vector<std::string> get_night_death_list() const {
        std::vector<std::string> ret;
        // save and guard simultaneously would die
        if (!poisoned_tonight &&
            !night[int(RoleT::WITCH)].empty() &&
            (night[int(RoleT::WITCH)] == night[int(RoleT::GUARDIAN)])) {
            ret.push_back(night[int(RoleT::WITCH)][0]);
        }
        // poisoned_tonight by witch
        if (poisoned_tonight && !night[int(RoleT::WITCH)].empty()) {
            ret.push_back(night[int(RoleT::WITCH)][0]);
        }
        // killed by wolf
        bool noguard = night[int(RoleT::GUARDIAN)].empty() && !night[int(RoleT::WOLF)].empty();
        bool guardwrong = !night[int(RoleT::GUARDIAN)].empty() && (night[int(RoleT::GUARDIAN)] != night[int(RoleT::WOLF)]);
        if (noguard || guardwrong) {
            ret.push_back(night[int(RoleT::WOLF)][0]);
        }
        return ret;
    }

    bool can_shoot() const {
        auto hunter = std::find_if(users.begin(), users.end(), [](auto pp) {
            return pp.second.role == int(RoleT::HUNTER);
        })->second;
        auto death_list = get_night_death_list();
        return (!poisoned_tonight || (night[int(RoleT::WITCH)][0] != hunter.userid)) &&
               (!hunter.lover || std::none_of(death_list.begin(), death_list.end(), [&hunter](auto id) {
                   return id == hunter.lover->userid;
               }));
    }

};

}
}

